﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using FreeDAM.WebUI.Areas.Admin.ViewModels;
using FreeDAM.Domain.Entities;
using FreeDAM.Domain.Abstract;
using System.IO;
using System.Data.Entity;

namespace FreeDAM.WebUI.Areas.Admin.Controllers
{
    public class AssetsController : Controller
    {
        private readonly IAssetRepository AssetRepository;
        private readonly IAssetToRenderRepository AssetToRenderRepository;
        private readonly IRenditionGroupRepository RenditionGroupRepository;
        private readonly IAssetHistoryRepository AssetHistoryRepository;
        private readonly ITagRepository TagRepository;

        public AssetsController(IAssetRepository assetRepository, IAssetToRenderRepository assetToRenderRepository,
            IRenditionGroupRepository renditionGroupRepository, IAssetHistoryRepository assetHistoryRepository,
            ITagRepository tagRepository)
        {
            AssetRepository = assetRepository;
            AssetToRenderRepository = assetToRenderRepository;
            RenditionGroupRepository = renditionGroupRepository;
            AssetHistoryRepository = assetHistoryRepository;
            TagRepository = tagRepository;
        }

        public ActionResult Index()
        {
            List<Asset> assets = AssetRepository.All.ToList();
            return View(assets);
        }

        public ActionResult Upload()
        {
            ViewBag.RenditionGroups = new SelectList(RenditionGroupRepository.All.AsNoTracking(), "Id", "Name");
            return View();
        }

        [HttpPost]
        public ActionResult Upload(AssetFileViewModel viewModel)
        {
            Asset asset;
            ActionResult result = View(viewModel);
            string uploadedFileName;

            if (ModelState.IsValid && viewModel.File.ContentLength > 0)
            {
                // check to see if the asset already exists
                // if exists, save off old "original" in version history and update item with new file
                uploadedFileName = Path.GetFileName(viewModel.File.FileName);
                if (AssetRepository.All.Count(a => a.FileName == uploadedFileName) > 0)
                {
                    asset = UpdateAsset(viewModel);
                }
                else // else create new asset
                {
                    asset = CreateNewAsset(viewModel);
                }

                // generate renditions
                AssetToRenderRepository.InsertOrUpdate(new AssetToRender { AssetId = asset.Id, Status = "Ready To Process" });
                AssetToRenderRepository.Save();

                RendererService.RenderServiceClient renderingClient = new RendererService.RenderServiceClient();
                renderingClient.CreateRenditions();

                result = RedirectToAction("Index");
            }
            else
            {
                ViewBag.RenditionGroups = new SelectList(RenditionGroupRepository.All.AsNoTracking(), "Id", "Name");
            }

            return result;
        }

        private Asset UpdateAsset(AssetFileViewModel viewModel)
        {
            string savedFilePath, archivedFilePath, uploadedFileName = Path.GetFileName(viewModel.File.FileName);
            Asset asset = AssetRepository.All.SingleOrDefault(a => a.FileName == uploadedFileName);
            AssetHistory assetHistory = new AssetHistory();

            // save off history
            assetHistory.AssetId = asset.Id;
            assetHistory.MimeType = asset.MimeType;
            assetHistory.FileName = asset.FileName;
            assetHistory.Name = asset.Name;
            AssetHistoryRepository.InsertOrUpdate(assetHistory);
            AssetHistoryRepository.Save();

            archivedFilePath = MoveAssetToHistory(asset, assetHistory.Id);
            assetHistory.DirectoryLocation = archivedFilePath;
            assetHistory.ServerRelativePath = "~/media/" + assetHistory.AssetId.ToString("D8") + "/Asset/History/" + assetHistory.Id.ToString("D8") + "/" + assetHistory.FileName;
            AssetHistoryRepository.InsertOrUpdate(assetHistory);
            AssetHistoryRepository.Save();

            // update asset
            asset.MimeType = Constants.MimeTypes[Path.GetExtension(viewModel.File.FileName).ToLower()];
            asset.RenditionGroupId = viewModel.RenditionGroup.Value;
            savedFilePath = SaveOffAsset(viewModel.File, asset.Id);
            asset.DirectoryLocation = Path.ChangeExtension(savedFilePath, Path.GetExtension(savedFilePath).ToLower());
            asset.FileName = Path.GetFileName(Path.ChangeExtension(savedFilePath, Path.GetExtension(savedFilePath).ToLower()));
            asset.ServerRelativePath = "~/media/" + asset.Id.ToString("D8") + "/Asset/" + asset.FileName;

            AssetRepository.InsertOrUpdate(asset);
            AssetRepository.Save();

            return asset;
        }

        private Asset CreateNewAsset(AssetFileViewModel viewModel)
        {
            string savedFilePath;
            Asset asset = new Asset();

            asset.MimeType = Constants.MimeTypes[Path.GetExtension(viewModel.File.FileName).ToLower()];
            asset.RenditionGroupId = viewModel.RenditionGroup.Value;
            AssetRepository.InsertOrUpdate(asset);
            AssetRepository.Save();

            savedFilePath = SaveOffAsset(viewModel.File, asset.Id);
            asset.DirectoryLocation = Path.ChangeExtension(savedFilePath, Path.GetExtension(savedFilePath).ToLower());
            asset.FileName = Path.GetFileName(Path.ChangeExtension(savedFilePath, Path.GetExtension(savedFilePath).ToLower()));
            asset.ServerRelativePath = "~/media/" + asset.Id.ToString("D8") + "/Asset/" + asset.FileName;
            AssetRepository.InsertOrUpdate(asset);
            AssetRepository.Save();

            return asset;
        }

        private string SaveOffAsset(HttpPostedFileBase httpPostedFileBase, int assetId)
        {
            string path, randomDirectory;
            string randomDirectoryName = assetId.ToString("D8");

            randomDirectory = Path.Combine(Server.MapPath("~/Media"), randomDirectoryName);
            path = Path.Combine(Path.Combine(randomDirectory, "Asset"), Path.GetFileName(httpPostedFileBase.FileName));
            if (!Directory.Exists(Path.Combine(randomDirectory, "Asset")))
            {
                Directory.CreateDirectory(Path.Combine(randomDirectory, "Asset"));
            }
            if (System.IO.File.Exists(path))
            {
                System.IO.File.Delete(path);
            }
            httpPostedFileBase.SaveAs(path);

            return path;
        }

        private string MoveAssetToHistory(Asset asset, int assetHistoryId)
        {
            string path, archiveDirectory;
            string archiveDirectoryName = assetHistoryId.ToString("D8");
            string assetDirectoryName = asset.Id.ToString("D8");

            archiveDirectory = Path.Combine(Server.MapPath("~/Media"), assetDirectoryName, "Asset", "History", archiveDirectoryName);
            path = Path.Combine(archiveDirectory, Path.GetFileName(asset.FileName));
            if (!Directory.Exists(archiveDirectory))
            {
                Directory.CreateDirectory(archiveDirectory);
            }
            System.IO.File.Move(asset.DirectoryLocation, path);

            return path;
        }

        public ActionResult Details(int id)
        {
            return View(AssetRepository.Find(id));
        }

        public ActionResult Preview(int id)
        {
            return PartialView(AssetRepository.Find(id));
        }

        public ActionResult Edit(int id)
        {
            Asset viewModel = AssetRepository.Find(id);
            ViewBag.Tags = new MultiSelectList(TagRepository.All.Where(t => t.TagCategory.Name != "Rendition Definition" || t.TagCategory == null).OrderBy(t => t.Name), "Id", "Name", viewModel.Tags.Select(t => t.Id));
            ViewBag.RenditionGroups = new SelectList(RenditionGroupRepository.All.AsNoTracking(), "Id", "Name");
            return View(viewModel);
        }

        [HttpPost]
        public ActionResult Edit(Asset viewModel, int[] tagIds)
        {
            ActionResult result = View(AssetRepository.Find(viewModel.Id));
            Asset asset;

            if (ModelState.IsValid)
            {
                asset = AssetRepository.Find(viewModel.Id);

                if (TryUpdateModel(asset))
                {
                    if (tagIds != null)
                    {
                        foreach (int tagId in tagIds)
                        {
                            if (asset.Tags.Count(t => t.Id == tagId) == 0)
                            {
                                asset.Tags.Add(TagRepository.Find(tagId));
                            }
                        }
                    }

                    asset.Tags.RemoveAll(t => !tagIds.Contains(t.Id));
                    AssetRepository.InsertOrUpdate(asset);
                    AssetRepository.Save();
                    result = RedirectToAction("Index");
                }
                else
                {
                    ViewBag.RenditionGroups = new SelectList(RenditionGroupRepository.All.AsNoTracking(), "Id", "Name");
                    ViewBag.Tags = new MultiSelectList(TagRepository.All.Where(t => t.TagCategory.Name != "Rendition Definition" || t.TagCategory == null).OrderBy(t => t.Name), "Id", "Name", viewModel.Tags.Select(t => t.Id));
                }
            }
            else
            {
                ViewBag.RenditionGroups = new SelectList(RenditionGroupRepository.All.AsNoTracking(), "Id", "Name");
                ViewBag.Tags = new MultiSelectList(TagRepository.All.Where(t => t.TagCategory.Name != "Rendition Definition" || t.TagCategory == null).OrderBy(t => t.Name), "Id", "Name", viewModel.Tags.Select(t => t.Id));
            }

            return result;
        }

        public ActionResult RegenerateRenditions(int id)
        {
            AssetToRenderRepository.InsertOrUpdate(new AssetToRender { AssetId = id, Status = "Ready To Process" });
            AssetToRenderRepository.Save();

            RendererService.RenderServiceClient renderingClient = new RendererService.RenderServiceClient();
            renderingClient.CreateRenditions();

            return RedirectToAction("Details", new { id = id });
        }
    }
}
